package utils.graph_utils;


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import utils.graph_utils.AStarSearch.Metric;

import components.LIFNeuron;
import components.Synapse;

public class Graph {

	private Metric metric;
	private ArrayList<Node> nodes;

	
	public Graph(LIFNeuron [] neurons, Metric metric) {
		this.metric = metric;
		nodes = new ArrayList<Node>(convertToGraph(neurons));
	}
	
//	public ArrayList<Node> convertToGraph (AbstractNeuron root) {
//		
//		HashMap<AbstractNeuron, Node> closed =
//				new HashMap<AbstractNeuron, Node> ();
//		
//		generateNode(root, closed);
//		
//		return new ArrayList<Node>(closed.values());
//		
//	}
	
	public Collection<Node> convertToGraph(LIFNeuron [] neurons) {
		
		HashMap<LIFNeuron, Node> nodeMapping = new HashMap<LIFNeuron, Node>();
	
		for(int i = 0, n = neurons.length; i < n; i++) {
			LIFNeuron ne = neurons[i];
			Node no = nodeMapping.get(ne);
			if(no == null) {
				no = new Node(ne);
				nodeMapping.put(ne, no);
			} 
			List<Synapse> syns = ne.getFanOut();			
			for(Synapse s: syns) {
				Edge e = new Edge(s);
				e.setSrc(no);
				LIFNeuron targ = (LIFNeuron) s.getTarg();
				Node t = nodeMapping.get(targ);
				if(t == null) {
					t = new Node(targ);
					nodeMapping.put(targ, t);		
				}
				e.setTarg(t);
				e.setCost(metric);
				t.getFanIn().add(e);
				no.getFanOut().add(e);
			}		
		}
		return nodeMapping.values();
	}
	
	
	
//	private Node generateNode(AbstractNeuron n, HashMap<AbstractNeuron, Node> closed) {
//		
//		Node node = new Node(n);
//		closed.put(n, node);
//		
//		for(Synapse s : n.getFanOut()) {
//			Edge e = new Edge(s);
//			e.setCost(metric);
//			AbstractNeuron targ = s.getTarg();
//			if (closed.get(targ) != null) {
//				e.setTarg(closed.get(targ));
//			} else {
//				e.setTarg(generateNode(n, closed));
//			}
//			node.getFanOut().add(e);
//		}	
//		
//		return node;
//		
//	}
	
	public void changeMetric(Metric metric) {
		this.metric = metric;
		updateEdges();
	}
	
	private void updateEdges() {
		for (Node n : nodes) {
			for (Edge e : n.getFanOut()) {
				e.setCost(metric);
			}
		}
		
	}

	public Metric getMetric() {
		return metric;
	}

	public void setMetric(Metric metric) {
		this.metric = metric;
	}

	public ArrayList<Node> getNodes() {
		return nodes;
	}

	public void setNodes(ArrayList<Node> nodes) {
		this.nodes = nodes;
	}
	
}
